/*
 * ============================================================================
 *
 *  [THC RPG] Total HardCore RPG
 *
 *  File:          core.inc
 *  Type:          Module
 *  Description:   Plugins Core Management
 *
 *  Copyright (C) 2009-2010  ArsiRC
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * ============================================================================
 */

/**
 * This module's identifier.
 */

/**
 * Register this module.
 */
Core_Register()
{
    // Define all the module's data as layed out by enum ModuleData in project.inc.
    new moduledata[ModuleData];
    
    moduledata[ModuleData_Disabled] = false;
    moduledata[ModuleData_Hidden] = false;
    strcopy(moduledata[ModuleData_FullName], MM_DATA_FULLNAME, "Core");
    strcopy(moduledata[ModuleData_ShortName], MM_DATA_SHORTNAME, "core");
    strcopy(moduledata[ModuleData_Description], MM_DATA_DESCRIPTION, "Plugins Core Management");
    // End the last one with INVALID_MODULE.
    moduledata[ModuleData_Dependencies][0] = INVALID_MODULE;
    
    // Send this array of data to the module manager.
    g_moduleCore = ModuleMgr_Register(moduledata);
    
    // Now register the events we're going to use.
    #if defined EVENT_MANAGER
        EventMgr_RegisterEvent(g_moduleCore, "Event_OnEventsRegister", "Core_OnEventsRegister");
    #endif

    // Register config file(s) that this module will use.
    #if defined CONFIG_MANAGER
        ConfigMgr_Register(g_moduleCore, "Core_OnConfigReload", "configs/thc_rpg/core.txt");
    #endif
}

/**
 * Register all events here.
 */
public Core_OnEventsRegister()
{
    // Register all the events needed for this module.
    EventMgr_RegisterEvent(g_moduleCore, "Event_OnAllPluginsLoaded",        "Core_OnAllPluginsLoaded");
    EventMgr_RegisterEvent(g_moduleCore, "Event_OnPluginEnd",               "Core_OnPluginEnd");
    EventMgr_RegisterEvent(g_moduleCore, "Event_OnMyModuleEnable",          "Core_OnMyModuleEnable");
    EventMgr_RegisterEvent(g_moduleCore, "Event_OnMyModuleDisable",         "Core_OnMyModuleDisable");
    EventMgr_RegisterEvent(g_moduleCore, "Event_OnMapStart",                "Core_OnMapStart");
    EventMgr_RegisterEvent(g_moduleCore, "Event_OnMapEnd",                  "Core_OnMapEnd");
    EventMgr_RegisterEvent(g_moduleCore, "Event_OnConfigsExecuted",         "Core_OnConfigsExecuted");
    EventMgr_RegisterEvent(g_moduleCore, "Event_OnClientAuthorized",        "Core_OnClientAuthorized");    
    EventMgr_RegisterEvent(g_moduleCore, "Event_OnClientDisconnect",        "Core_OnClientDisconnect");
    #if defined PROJECT_GAME_CSS
        //EventMgr_RegisterEvent(g_moduleCore, "Event_RoundStart",              "Core_RoundStart");
        EventMgr_RegisterEvent(g_moduleCore, "Event_RoundEnd",                  "Core_RoundEnd");
        EventMgr_RegisterEvent(g_moduleCore, "Event_PlayerTeam",                "Core_PlayerTeam");
        EventMgr_RegisterEvent(g_moduleCore, "Event_PlayerHurt",                "Core_PlayerHurt");
        EventMgr_RegisterEvent(g_moduleCore, "Event_PlayerDeath",               "Core_PlayerDeath");
        EventMgr_RegisterEvent(g_moduleCore, "Event_BombPlanted",               "Core_BombPlanted");
        EventMgr_RegisterEvent(g_moduleCore, "Event_BombDefused",               "Core_BombDefused");
        EventMgr_RegisterEvent(g_moduleCore, "Event_BombExploded",              "Core_BombExploded");
        EventMgr_RegisterEvent(g_moduleCore, "Event_HostageRescued",            "Core_HostageRescued");
        EventMgr_RegisterEvent(g_moduleCore, "Event_HostageRescuedAll",         "Core_HostageRescuedAll");
    #endif
}

/**
 * All modules and events have been registered by this point.  Event priority can be changed here.
 */
public Core_OnEventsReady()
{
}

#if defined CONFIG_MANAGER
/**
 * Called when a registered config file (by this module) is manually reloaded.
 */
public Core_OnConfigReload(configindex)
{
    #if defined LOG_MANAGER
        LogMgr_Print(g_moduleCore, LogType_Normal, "CoreConfigReload", "Reloaded core module's config (index %d)", configindex);
    #endif

    ConfigMgr_CacheKv(g_moduleCore, CM_CONFIGINDEX_FIRST, "CoreModule_ConfigCache");
}

/**
  * Read config values
  */
public KvCache:CoreModule_ConfigCache(Handle:kv, sectionindex, const String:sectionname[])
{
    // Read Core config
    if(StrEqual(sectionname, "core"))
    {
        g_CoreConfigCache[CoreConfig_bot_enable] = KvGetNum(kv, "bot_enable");
        g_CoreConfigCache[CoreConfig_bot_control] = KvGetNum(kv, "bot_control");
        g_CoreConfigCache[CoreConfig_bot_maxlevel] = KvGetNum(kv, "bot_maxlevel");
        g_CoreConfigCache[CoreConfig_exp_max] = KvGetNum(kv, "exp_max");
        g_CoreConfigCache[CoreConfig_exp_start] = KvGetNum(kv, "exp_start");
        g_CoreConfigCache[CoreConfig_exp_inc] = KvGetNum(kv, "exp_inc");
        g_CoreConfigCache[CoreConfig_exp_kill] = KvGetNum(kv, "exp_kill");
        g_CoreConfigCache[CoreConfig_exp_headshot] = KvGetNum(kv, "exp_headshot");
        g_CoreConfigCache[CoreConfig_exp_damage] = KvGetFloat(kv, "exp_damage");
        g_CoreConfigCache[CoreConfig_exp_knifedmg] = KvGetFloat(kv, "exp_knifedmg");
        g_CoreConfigCache[CoreConfig_exp_objectives] = KvGetNum(kv, "exp_objectives");
        g_CoreConfigCache[CoreConfig_exp_teamwin] = KvGetFloat(kv, "exp_teamwin");
        g_CoreConfigCache[CoreConfig_exp_bombplanted] = KvGetFloat(kv, "exp_bombplanted");
        g_CoreConfigCache[CoreConfig_exp_bombdefused] = KvGetFloat(kv, "exp_bombdefused");
        g_CoreConfigCache[CoreConfig_exp_bombexploded] = KvGetFloat(kv, "exp_bombexploded");
        g_CoreConfigCache[CoreConfig_exp_hostage] = KvGetFloat(kv, "exp_hostage");
        g_CoreConfigCache[CoreConfig_credits_inc] = KvGetNum(kv, "credits_inc");
        g_CoreConfigCache[CoreConfig_credits_start] = KvGetNum(kv, "credits_start");
        g_CoreConfigCache[CoreConfig_save_percent] = KvGetFloat(kv, "save_percent");
        g_CoreConfigCache[CoreConfig_advert] = KvGetNum(kv, "advert");
        g_CoreConfigCache[CoreConfig_announcements] = KvGetNum(kv, "announcements");
        g_CoreConfigCache[CoreConfig_freeforall] = KvGetNum(kv, "freeforall");
    }
}
#endif

/**
 * All plugins have loaded.
 */
public Core_OnAllPluginsLoaded()
{
    InitPlayersVector(); 	// Create vector to hold players data

    InitOffsets(); 			// Find offsets and defines functions to use them

    HookCmds(); 			// Hook commands
    HookAdminCmds(); 		// Hook Admincommands

    CreateAdminMenu(); 		// Create admin menu

    ConnectSQL(); 			// Connect to SQL database
}

/**
 * Plugin is ending.
 */
public Core_OnPluginEnd()
{
    new size=GetArraySize(vecPlayers);
    for(new x=size-1;x>=0;x--)
    {
        new client=GetArrayCell(GetArrayCell(vecPlayers,x),VECTOR_PLAYERS_CLIENT);
        if(client)
        {
            SavePlayerData(client);
            RemoveFromArray(vecPlayers,x);
        }
    }
}

/**
 * The module that hooked this event callback has been enabled.
 */
public Core_OnMyModuleEnable()
{
    ClearPlayersVector();	// Clear vector that hold players data

    for(new client=1;client<=MaxClients;client++)
    {
        if(IsClientAuthorized(client))
        {
            InitPlayer(client);
            LoadPlayerData(client);
        }
    }
}

/**
 * The module that hooked this event callback has been disabled.
 */
public Core_OnMyModuleDisable()
{
    new size=GetArraySize(vecPlayers);
    for(new x=size-1;x>=0;x--)
    {
        new client=GetArrayCell(GetArrayCell(vecPlayers,x),VECTOR_PLAYERS_CLIENT);
        if(client)
        {
            SavePlayerData(client);
            RemoveFromArray(vecPlayers,x);
        }
    }
}

/**
 * The map has started.
 */
public Core_OnMapStart()
{
    #if defined CONFIG_MANAGER
        ConfigMgr_CacheKv(g_moduleCore, CM_CONFIGINDEX_FIRST, "CoreModule_ConfigCache");
    #endif

    ClearPlayersVector();
    for(new client=1;client<=MaxClients;client++)
    {
        if(IsClientAuthorized(client))
        {
            InitPlayer(client);
            LoadPlayerData(client);
        }
    }
}

/**
 * The map has ended.
 */
public Core_OnMapEnd()
{
    new size=GetArraySize(vecPlayers);
    for(new x=size-1;x>=0;x--)
    {
        new client=GetArrayCell(GetArrayCell(vecPlayers,x),VECTOR_PLAYERS_CLIENT);
        if(client)
        {
            SavePlayerData(client);
            RemoveFromArray(vecPlayers,x);
        }
    }
}

/**
 * All convars are set, cvar-dependent code should use this.
 */
public Core_OnConfigsExecuted()
{
    // This should fix the a2s_rules bug on linux srcds
    #if defined PROJECT_PUBLIC_CVAR
        decl String:publiccvarvalue[32];
        strcopy(publiccvarvalue, sizeof(publiccvarvalue), PROJECT_VERSION);
        SetConVarString(FindConVar(PROJECT_PUBLIC_CVAR), publiccvarvalue);
    #endif
    
    PrecacheModels();
    ManageSQLTable(1);		// create SQL table if it is not existing
    CheckDBUpdate();        // update the database to actual version
}

/**
 * Client got Steam-Id
 * 
 * @param client    The client index.
 * @param auth      Client Auth String
 */
public Core_OnClientAuthorized(client, const String:auth[])
{
    InitPlayer(client);
    LoadPlayerData(client);    
}

/**
 * Client is disconnecting from the server.
 * 
 * @param client    The client index.
 */
public Core_OnClientDisconnect(client)
{
    new clientpos=SavePlayerData(client);
    if(clientpos!=-1)
        RemoveFromArray(vecPlayers,clientpos);
}

/**
 * Round has started.
 */
public Core_RoundStart()
{
}

/**
 * Round has ended.
 */
public Core_RoundEnd()
{
    for(new client=1;client<=MaxClients;client++)
        if(IsClientAuthorized(client))
            SavePlayerData(client);
}

/**
 * Client has joined a team.
 * 
 * @param client        The client index.
 * @param team          The client's new team.
 * @param oldteam       The team the client is switching from.
 * @param disconnect    True if the client is disconnecting, false if switching to a team.
 */
public Core_PlayerTeam(client, team, oldteam, bool:disconnect)
{
    if(g_CoreConfigCache[CoreConfig_advert]&&!disconnect)
        TransMgr_PrintText(client, MsgFormat_None, MsgType_Chat, INVALID_MODULE, false, "Advert", PROJECT_VERSION);
}

/**
 * Client has been damaged.
 * 
 * @param victim        The index of the hurt client.
 * @param attacker      The index of the attacking client.
 * @param health        How much health the client has after the damage.
 * @param armor         How much armor the client has after the damage.
 * @param weapon        The weapon classname used to hurt the victim. (No weapon_ prefix)
 * @param dmg_health    The amount of health the victim lost.
 * @param dmg_armor     The amount of armor the victim lost.
 * @param hitgroup      The hitgroup index of the victim that was damaged.
 */
public Core_PlayerHurt(victim, attacker, health, armor, const String:weapon[], dmg_health, dmg_armor, hitgroup)
{
    if(victim&&attacker&&victim!=attacker&&(GetClientTeam(victim)!=GetClientTeam(attacker)||g_CoreConfigCache[CoreConfig_freeforall]))
    {
        if(!StrEqual(weapon,"knife",false))
            SetXP(attacker,RoundToNearest(g_CoreConfigCache[CoreConfig_exp_damage]*float(dmg_health)),false);
        else
            SetXP(attacker,RoundToNearest(g_CoreConfigCache[CoreConfig_exp_knifedmg]*float(dmg_health)),false);
    }
}

/**
 * Client has been killed.
 *
 * @param victim    The index of the killed client.
 * @param attacker  The killer of the victim.
 * @param weapon    The weapon classname used to kill the victim. (No weapon_ prefix)
 * @param headshot  True if the death was by headshot, false if not.
 */
public Core_PlayerDeath(victim, attacker, const String:weapon[], bool:headshot)
{
    if(victim&&attacker&&victim!=attacker&&(GetClientTeam(victim)!=GetClientTeam(attacker)||g_CoreConfigCache[CoreConfig_freeforall]))
    {
        new xp=g_CoreConfigCache[CoreConfig_exp_kill]*GetLevel(victim);
        if(headshot)
            xp+=g_CoreConfigCache[CoreConfig_exp_headshot];
        SetXP(attacker,xp,g_CoreConfigCache[CoreConfig_announcements]);
    }
}

/**
 * Client has planted the bomb.
 *
 * @param planter	The client who has planted the bomb
 */
public Core_BombPlanted(planter)
{
    if(g_CoreConfigCache[CoreConfig_exp_objectives])
        SetXP(planter,RoundToNearest(TeamRatio(2)*float(GetReqXP(planter))*g_CoreConfigCache[CoreConfig_exp_bombplanted]),g_CoreConfigCache[CoreConfig_announcements]);
}

/**
 * Client has defused the bomb.
 *
 * @param defuser	The client who has defused the bomb
 */
public Core_BombDefused(defuser)
{
    if(g_CoreConfigCache[CoreConfig_exp_objectives])
    {
        SetXP(defuser,RoundToNearest(TeamRatio(3)*float(GetReqXP(defuser))*g_CoreConfigCache[CoreConfig_exp_bombdefused]),g_CoreConfigCache[CoreConfig_announcements]);
        TeamWinXP(defuser,3);
    }
}

/**
 * The bomb has exploded.
 *
 * @param planter	The client who has planted the bomb
 */
public Core_BombExploded(planter)
{
    if(g_CoreConfigCache[CoreConfig_exp_objectives])
    {
        SetXP(planter,RoundToNearest(TeamRatio(2)*float(GetReqXP(planter))*g_CoreConfigCache[CoreConfig_exp_bombexploded]),g_CoreConfigCache[CoreConfig_announcements]);
        TeamWinXP(planter,2);
    }
}

/**
 * Client has rescued a hostage.
 *
 * @param rescuer	The client who has rescued a hostage
 */
public Core_HostageRescued(rescuer)
{
    if(g_CoreConfigCache[CoreConfig_exp_objectives])
        SetXP(rescuer,RoundToNearest(TeamRatio(3)*float(GetReqXP(rescuer))*g_CoreConfigCache[CoreConfig_exp_hostage]),g_CoreConfigCache[CoreConfig_announcements]);
}

/**
 * All hostages got rescued.
 */
public Core_HostageRescuedAll()
{
    if(g_CoreConfigCache[CoreConfig_exp_objectives])
        for(new x=1;x<=MaxClients;x++)
            if(IsClientInGame(x))
                if(GetClientTeam(x)==3)
                    TeamWinXP(x,3);
}
